// 公共函数库
var stock_pub = {
    urlParams: function (name) {
        // 获取URL中的查询参数
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get(name);
    },

    // yyyyMMdd 转 Date
    parseYYYYMMDD: function (dateString) {
        // 验证输入格式是否为8位纯数字
        if (typeof dateString !== 'string' || dateString.length !== 8 || !/^\d+$/.test(dateString)) {
            return new Date(NaN); // 返回无效日期
        }

        // 提取年月日并转为整数
        const year = parseInt(dateString.substring(0, 4), 10);
        const month = parseInt(dateString.substring(4, 6), 10) - 1; // 月份需减1（0-11）
        const day = parseInt(dateString.substring(6, 8), 10);

        // 创建日期对象
        const date = new Date(year, month, day);

        return date;
    },
    // 格式化为 yyyyMMdd（可选）
    formatYYYYMMDD: function (date) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}${month}${day}`;
    },
    // 封装请求函数
    fetchStockData: async function (ts_code, callback) {
        $.ajax({
            type: 'GET',
            url: `/api/stock/stock_view.json?action=stock_daily&ts_code=${ts_code}`,
            processData: false,
            contentType: false,
            dataType: "json",
            success: function (res) {
                if ($.yqDataErrCheck(res)) {
                    callback(res);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {
                console.log(XMLHttpRequest.responseText);
                callback({ "error": 99, "info": errorThrown });
            }
        });
    }
}

//图表计算函数
var calculate = {
    // 计算移动平均线
    MA: function (data, period, i = 4) {
        return data.map((item, index) => {
            if (index < period - 1) return null;
            const sum = data.slice(index - period + 1, index + 1)
                .reduce((acc, val) => acc + val[i], 0);
            //return [item[0], parseFloat((sum / period).toFixed(2))];
            return [item[0], (sum / period)];
        }).filter(Boolean);
    },
    // 添加MACD计算函数 index 1:开盘价 4:收盘价 5:成交量
    calculateMACD: function (data, shortPeriod = 12, longPeriod = 26, signalPeriod = 9, index = 4) {
        const shortEMA = [];
        const longEMA = [];
        const macdLine = [];
        const signalLine = [];
        const histogram = [];

        // 计算短期EMA
        for (let i = 0; i < data.length; i++) {
            if (i === 0) {
                shortEMA[i] = data[i][index];
            } else {
                const multiplier = 2 / (shortPeriod + 1);
                shortEMA[i] = (data[i][index] - shortEMA[i - 1]) * multiplier + shortEMA[i - 1];
            }
        }

        // 计算长期EMA
        for (let i = 0; i < data.length; i++) {
            if (i === 0) {
                longEMA[i] = data[i][index];
            } else {
                const multiplier = 2 / (longPeriod + 1);
                longEMA[i] = (data[i][index] - longEMA[i - 1]) * multiplier + longEMA[i - 1];
            }
        }

        // 计算MACD线
        for (let i = 0; i < data.length; i++) {
            macdLine[i] = [data[i][0], shortEMA[i] - longEMA[i]];
        }

        // 计算信号线
        for (let i = 0; i < data.length; i++) {
            if (i === 0) {
                signalLine[i] = macdLine[i][1];
            } else {
                const multiplier = 2 / (signalPeriod + 1);
                signalLine[i] = (macdLine[i][1] - signalLine[i - 1]) * multiplier + signalLine[i - 1];
            }
        }

        for (let i = 0; i < data.length; i++) {
            signalLine[i] = [data[i][0], signalLine[i]];
        }

        // 计算柱状图
        for (let i = 0; i < data.length; i++) {
            histogram[i] = [data[i][0], macdLine[i][1] - signalLine[i][1]];
        }

        return {
            macdLine, // diff 就是 MACD 线
            signalLine, // dea 就是信号线
            histogram
        };
    },
    // 计算ENE指标
    ENE: function (data, M1 = 11, M2 = 9, N = 10, i = 4) {
        const result = [];
        const calculateSMA = (data, period) => {
            return data.map((item, index) => {
                if (index < period - 1) return null;
                const sum = data.slice(index - period + 1, index + 1)
                    .reduce((acc, val) => acc + val[i], 0);
                return sum / period;
            });
        };
        const sma = calculateSMA(data, N);
        data.forEach((item, index) => {
            if (sma[index] !== null) {
                const upper = (1 + M1 / 100) * sma[index];
                const lower = (1 - M2 / 100) * sma[index];
                const ene = (upper + lower) / 2;
                result.push([item[0], parseFloat(ene.toFixed(2)), parseFloat(upper.toFixed(2)), parseFloat(lower.toFixed(2))]);
            }
        });
        return result;
    },
    // 计算布林带指标
    BOLL: function (data, period = 20, multiplier = 2, i = 4) {
        return data.map((item, index) => {
            if (index < period - 1) return null;
            const slice = data.slice(index - period + 1, index + 1);
            const sum = slice.reduce((acc, val) => acc + val[i], 0);
            const ma = sum / period;
            const squaredDifferences = slice.map(val => Math.pow(val[i] - ma, 2));
            const variance = squaredDifferences.reduce((acc, val) => acc + val, 0) / period;
            const stdDev = Math.sqrt(variance);
            const upper = ma + (multiplier * stdDev);
            const lower = ma - (multiplier * stdDev);
            return [item[0], parseFloat(ma.toFixed(2)), parseFloat(upper.toFixed(2)), parseFloat(lower.toFixed(2))];
        }).filter(Boolean);
    },
    // 添加KDJ计算函数 价格波动，无关开盘、收盘价格
    calculateKDJ: function (data, period = 9, kSmooth = 3, dSmooth = 3, index = 4) {
        const kdjValues = [];
        for (let i = 0; i < data.length; i++) {
            if (i < period - 1) {
                kdjValues.push(null);
                continue;
            }
            let lowestLow = Infinity;
            let highestHigh = -Infinity;
            for (let j = i - period + 1; j <= i; j++) {
                lowestLow = Math.min(lowestLow, data[j][3]);
                highestHigh = Math.max(highestHigh, data[j][2]);
            }
            const close = data[i][index];
            const rsv = (close - lowestLow) / (highestHigh - lowestLow) * 100;
            const k = i === period - 1 ? 50 : (kSmooth - 1) / kSmooth * (kdjValues[i - 1]?.K || 50) + 1 / kSmooth * rsv;
            const d = i === period - 1 ? 50 : (dSmooth - 1) / dSmooth * (kdjValues[i - 1]?.D || 50) + 1 / dSmooth * k;
            const j = 3 * k - 2 * d;
            kdjValues.push({
                x: data[i][0],
                K: k,
                D: d,
                J: j
            });
        }
        return kdjValues;
    },
    //  CCI 指标计算函数
    calculateCCI: function (ohlc, period) {
        const typicalPrices = [];
        const smaValues = [];
        const meanDeviations = [];
        const cciValues = [];

        // 计算典型价格（Typical Price）
        ohlc.forEach(([timestamp, open, high, low, close]) => {
            const typicalPrice = (high + low + close) / 3;
            typicalPrices.push([timestamp, typicalPrice]);
        });

        // 计算简单移动平均线（SMA）
        for (let i = 0; i < typicalPrices.length; i++) {
            if (i < period - 1) {
                smaValues.push(null);
            } else {
                let sum = 0;
                for (let j = 0; j < period; j++) {
                    sum += typicalPrices[i - j][1];
                }
                const sma = sum / period;
                smaValues.push([typicalPrices[i][0], sma]);
            }
        }

        // 计算平均偏差（Mean Deviation）
        for (let i = 0; i < typicalPrices.length; i++) {
            if (i < period - 1) {
                meanDeviations.push(null);
            } else {
                let sumDeviations = 0;
                for (let j = 0; j < period; j++) {
                    const deviation = Math.abs(typicalPrices[i - j][1] - smaValues[i][1]);
                    sumDeviations += deviation;
                }
                const meanDeviation = sumDeviations / period;
                meanDeviations.push([typicalPrices[i][0], meanDeviation]);
            }
        }

        // 计算 CCI 值
        for (let i = 0; i < typicalPrices.length; i++) {
            if (!(i < period - 1)) {
                const cci = (typicalPrices[i][1] - smaValues[i][1]) / (0.015 * meanDeviations[i][1]);
                cciValues.push([typicalPrices[i][0], cci]);
            }
        }

        return cciValues;
    },
    //  ADX 指标计算函数
    calculateADX: function (data, period) {
        let trValues = [];
        let dmPlusValues = [];
        let dmMinusValues = [];
        let trSMA = 0;
        let dmPlusSMA = 0;
        let dmMinusSMA = 0;
        let diPlusValues = [];
        let diMinusValues = [];
        let dxValues = [];
        let adxValues = [];

        // 计算 TR, +DM, -DM
        for (let i = 1; i < data.length; i++) {
            const high = data[i][1];
            const low = data[i][2];
            const prevClose = data[i - 1][3];
            const trueRange = Math.max(high - low, Math.abs(high - prevClose), Math.abs(low - prevClose));
            trValues.push(trueRange);

            const upMove = high - data[i - 1][1];
            const downMove = data[i - 1][2] - low;
            const dmPlus = upMove > downMove && upMove > 0 ? upMove : 0;
            const dmMinus = downMove > upMove && downMove > 0 ? downMove : 0;
            dmPlusValues.push(dmPlus);
            dmMinusValues.push(dmMinus);
        }

        // 计算 TR, +DM, -DM 的 SMA
        trSMA = trValues.slice(0, period).reduce((a, b) => a + b, 0) / period;
        dmPlusSMA = dmPlusValues.slice(0, period).reduce((a, b) => a + b, 0) / period;
        dmMinusSMA = dmMinusValues.slice(0, period).reduce((a, b) => a + b, 0) / period;

        diPlusValues.push((dmPlusSMA / trSMA) * 100);
        diMinusValues.push((dmMinusSMA / trSMA) * 100);
        dxValues.push((Math.abs(diPlusValues[0] - diMinusValues[0]) / (diPlusValues[0] + diMinusValues[0])) * 100);

        // 计算后续的 TR, +DM, -DM 的平滑值
        for (let i = period; i < data.length - 1; i++) {
            trSMA = (trSMA * (period - 1) + trValues[i]) / period;
            dmPlusSMA = (dmPlusSMA * (period - 1) + dmPlusValues[i]) / period;
            dmMinusSMA = (dmMinusSMA * (period - 1) + dmMinusValues[i]) / period;

            const diPlus = (dmPlusSMA / trSMA) * 100;
            const diMinus = (dmMinusSMA / trSMA) * 100;
            const dx = (Math.abs(diPlus - diMinus) / (diPlus + diMinus)) * 100;

            diPlusValues.push(diPlus);
            diMinusValues.push(diMinus);
            dxValues.push(dx);
        }

        // 计算 ADX
        let adxSMA = dxValues.slice(0, period).reduce((a, b) => a + b, 0) / period;
        adxValues.push(adxSMA);

        for (let i = period; i < dxValues.length; i++) {
            adxSMA = (adxSMA * (period - 1) + dxValues[i]) / period;
            adxValues.push(adxSMA);
        }

        // 组合时间戳和 ADX 值
        const result = [];
        for (let i = 0; i < adxValues.length; i++) {
            result.push([data[i + 2 * period - 1][0], adxValues[i]]);
        }

        const plus_result = [];
        const minus_result = [];
        for (let i = 0; i < diPlusValues.length; i++) {
            plus_result.push([data[i + period][0], diPlusValues[i]]);
        }
        for (let i = 0; i < diMinusValues.length; i++) {
            minus_result.push([data[i + period][0], diMinusValues[i]]);
        }

        return [result, plus_result, minus_result];
    },
    // 计算平均真实波幅（ATR）
    calculateATR: function (ohlc, period) {
        function calculateTR(high, low, prevClose) {
            return Math.max(high - low, Math.abs(high - prevClose), Math.abs(low - prevClose));
        }
        var atrValues = [];
        var firstATR = calculateTR(ohlc[0][2], ohlc[0][3], ohlc[0][4]);
        atrValues.push([ohlc[0][0], firstATR]);
        for (var i = 1; i < ohlc.length; i++) {
            var currentTR = calculateTR(ohlc[i][2], ohlc[i][3], ohlc[i - 1][4]);
            var atr = (atrValues[i - 1] * (period - 1) + currentTR) / period;
            atrValues.push([ohlc[i][0], atr]);
        }
        return atrValues[atrValues.length - 1];
    },
    // 计算RSI指标
    calculateRSI: function (data, period = 14, is_vol = false) { // 默认周期为14
        const rsiValues = [];
        const gains = [];
        const losses = [];

        // 计算价格变动
        for (let i = 1; i < data.length; i++) {
            var change = data[i][4] - data[i - 1][4]; // 假设数据格式为 [timestamp, open, high, low, close,vol]
            if (is_vol) {
                change = change * data[i][5];
            }
            gains.push(Math.max(change, 0));
            losses.push(Math.max(-change, 0));
        }

        // 计算初始平均上涨和平均下跌
        let avgGain = gains.slice(0, period).reduce((sum, val) => sum + val, 0) / period;
        let avgLoss = losses.slice(0, period).reduce((sum, val) => sum + val, 0) / period;

        // 计算初始RSI值
        const rs = avgGain / avgLoss;
        rsiValues.push([data[period][0], 100 - (100 / (1 + rs))]);

        // 计算后续RSI值
        for (let i = period; i < gains.length; i++) {
            avgGain = ((avgGain * (period - 1)) + gains[i]) / period;
            avgLoss = ((avgLoss * (period - 1)) + losses[i]) / period;
            const rs = avgGain / avgLoss;
            rsiValues.push([data[i + 1][0], 100 - (100 / (1 + rs))]);
        }
        return rsiValues;
    },
    /* 累积派发线（Accumulation/Distribution Line）
       原理：通过收盘价在日内区间的位置，计算资金累积强度。
        资金流系数 = (收盘价-最低价) - (最高价-收盘价) / (最高价-最低价)  
        当日AD值 = 资金流系数 × 成交量  
        累积AD线 = 前日AD值 + 当日AD值  
    */
    calculateAD: function (ohlc) {
        let adValues = [];
        let previousAD = 0;

        for (let i = 0; i < ohlc.length; i++) {
            const [timestamp, open, high, low, close, volume] = ohlc[i];
            // 处理第一天没有前日 AD 值的情况
            if (i === 0) {
                previousAD = 0;
            }
            // 计算资金流系数
            const moneyFlowMultiplier = (high - low) > 0 ? ((close - low) - (high - close)) / (high - low) : 0;
            // 计算当日 AD 值
            const dailyAD = moneyFlowMultiplier * volume;
            // 计算累积 AD 线
            const cumulativeAD = previousAD + dailyAD;
            adValues.push([timestamp, cumulativeAD]);
            // 更新前日 AD 值
            previousAD = cumulativeAD;
        }

        return adValues;
    },
    /*
        突击能量比（关键启动信号）
        原理：结合价格涨幅与成交额爆发，识别主力突击强度
        公式：突击能量比 = 本K线涨幅(%) × 成交额/前5根K线平均成交额
    */
    calculateSER: function (ohlc, period = 5) {
        const ratios = [];

        if (ohlc.length < period) {
            return ratios;
        }

        for (let i = period; i < ohlc.length; i++) {
            const currentBar = ohlc[i];
            const currentClose = currentBar[4];
            const currentOpen = currentBar[1];
            const currentVolume = currentBar[5];

            // 计算本 K 线涨幅百分比 
            const priceChangePercentage = ((currentClose - currentOpen) / currentOpen) * 100;

            // 计算前 5 根 K 线平均成交额
            let totalVolume = 0;
            for (let j = i - period; j < i; j++) {
                totalVolume += ohlc[j][5];
            }
            const averageVolume = totalVolume / period;

            // 计算突击能量比
            const vol = priceChangePercentage * (currentVolume / averageVolume);
            ratios.push([currentBar[0], parseFloat(vol.toFixed(2))]);
        }
        return ratios;
    },
    /*
        量价加速度 = (∑(单K线涨幅 × 该K线成交额)/∑(成交额))100
        *计算步骤：
        计算最近6根K线每根的能量系数：涨幅 × 成交额
        求和能量系数 ÷ 总成交额 × 100
    */
    calculatePVA: function (ohlc, period = 6) {
        const ratios = [];

        if (ohlc.length < period) {
            return ratios;
        }

        for (let j = period; j < ohlc.length; j++) {
            const lastPeriodKLines = ohlc.slice(j - period, j);
            let totalEnergyCoefficient = 0;
            let totalVolume = 0;

            for (let i = 0; i < lastPeriodKLines.length; i++) {
                const [, open, , , close, volume] = lastPeriodKLines[i];
                const priceChange = ((close - open) / open); // 单 K 线涨幅百分比
                const energyCoefficient = priceChange * volume;

                totalEnergyCoefficient += energyCoefficient;
                totalVolume += volume;
            }

            const vol = (totalEnergyCoefficient / totalVolume) * 100;
            ratios.push([ohlc[j][0],parseFloat(vol.toFixed(2))]);
        }

        return ratios;
    },
    /*
        资金聚焦度
        原理：通过成交额集中度判断资金攻击方向
        公式：多头聚焦度 = (上涨K线成交额总和/近6根K线总成交额)*100
    */
    calculateBF: function (ohlc, period = 6) {
        const ratios = [];
        if (ohlc.length < 6) {
            return ratios;
        }

        for (let j = period; j < ohlc.length; j++) {
            const last6KLines = ohlc.slice(j - period, j);

            let risingVolumeSum = 0;
            let totalVolume = 0;

            // 遍历最近6根K线数据
            for (let i = 0; i < last6KLines.length; i++) {
                const [, open, , , close, volume] = last6KLines[i];
                totalVolume += volume;
                // 判断是否为上涨K线
                if (close > open) {
                    risingVolumeSum += volume;
                }
            }

            const vol = (risingVolumeSum / totalVolume) * 100;
            ratios.push([ohlc[j][0], parseFloat(vol.toFixed(2))]);
        }

        return ratios;
    },

    // 取得对应数值数据
    getSelectByDate: function (lists, date) {
        var r_list = [];
        for (let i = 0; i < lists.length; i++) {
            if (lists[i][0] === date) {
                r_list = lists[i];
                break;
            }
        }
        return r_list;
    },

    // 取得对应数值数据
    getEndDatasByDate: function (lists, date, n = -1) {
        var r_lists = [];
        for (let i = 0; i < lists.length; i++) {
            r_lists.push(lists[i]);
            if (lists[i][0] === date) {
                break;
            }
        }

        if (n > 0 && r_lists.length >= n) {
            r_lists = r_lists.slice(r_lists.length - n, r_lists.length);
        }
        return r_lists;
    },

    // 判断涨跌停的函数
    // 1:涨停 0:正常 -1:跌停
    checkLimitStatus: function (prevClose, currentClose) {
        // 假设涨跌停幅度为 10%，可根据实际情况调整
        const limitUp = prevClose * 1.1;
        const limitDown = prevClose * 0.9;

        if (currentClose >= limitUp) {
            return 1;
        } else if (currentClose <= limitDown) {
            return -1;
        } else {
            return 0;
        }
    },
    /*
     * 计算价格变化角度
     * @param {Array<number>} prices - 价格数组
     * @param {number} [i = prices.length - 1] - 要计算的索引，默认为数组最后一个元素
     * @returns {number} - 角度值
    */
    price_angles: function (prices) {
        var angles = [];

        for (let j = 0; j < prices.length; j++) {
            if (j == 0) {
                angles.push([prices[j][0], 0]);
                continue;
            }

            const delta = (prices[j][1] - prices[j - 1][1]) * 2 / (prices[j][1] + prices[j - 1][1]);
            angles.push([prices[j][0], (Math.atan(delta) * 180) / Math.PI]);
        }

        return angles;
    },
    /**
     * 计算角度变化
     * @param {Array<number>} angles - 价格变化角度
     * @param {number} [i = 4] - 要计算的索引，默认为数组最后一个元素
     * @returns {number} [n = 5] - 跨度
    */
    price_angles_trend: function (angles, n = 5) {
        let trend = [];
        for (let j = 0; j < angles.length; j++) {
            if (j == 0) {
                trend.push([angles[j][0], 0]);
                continue;
            }

            let num = 0;
            for (let i = 0; i < n; i++) {
                if (j - i <= 0) {
                    break;
                }
                num += (angles[j - i][1] - angles[j - i - 1][1]);
            }

            trend.push([angles[j][0], num]);
        }
        return trend;
    }
}
